mapcar = function(fn, iter) {
  for (var i=0; i < iter.length; i++) {
    fn(iter[i]);
  }
};

bind = function(fn, self) {
  if (typeof(fn) == "string") {
    fn = self[fn];
  }
  return function() {
    fn.apply(self, arguments);
  };
};

Deferred = function() {
  this.callbacks = [];
  this.errbacks = [];
};

Deferred.prototype.addCallback = function(fn) {
  this.callbacks.push(fn);
  try {
    if (typeof(this._result) != "undefined") {
      fn(this._result);
    }
  } catch (err) {
    this.errback(err);
  }
  return this;
};

Deferred.prototype.addErrback = function(fn) {
  this.errbacks.push(fn);
  if (typeof(this._error) != "undefined") {
    fn(this._error);
  }
  return this;
};

Deferred.prototype.callback = function(result) {
  try {
    mapcar(
      function(fn) {
        fn(result);
      },
      this.callbacks);
  } catch (err) {
    this.errback(err);
  }
  this._result = result;
};

Deferred.prototype.errback = function(result) {
  mapcar(
    function(fn) {
      fn(result);
    },
    this.errbacks);
  this._error = result;
};

xml = {};

xml.parseFromString = function(content) {
  return (new DOMParser()).parseFromString(content, "text/xml");
};

xml.objectify = function(node) {
  if (node.nodeType == 3 || node.nodeType == 4) {
    return node.nodeValue;
  }
  if (node.nodeType == 1 || node.nodeType == 9) {
    var obj = {};
    var count = 0;
    if (node.attributes) {
      for (var i = 0; i < node.attributes.length; i++) {
        var attr = node.attributes[i];
        if (attr.specified) {
          obj[attr.name] = attr.value;
          count++;
        }
      }
    }
    if (node.childNodes) {
      for (var i = 0; i < node.childNodes.length; i++) {
        count++;
        var nodeName = node.childNodes[i].nodeName;
        if (typeof(obj[nodeName]) == "undefined") {
          obj[nodeName] = xml.objectify(node.childNodes[i]);
        } else {
          if (obj[nodeName].constructor != Array) {
            obj[nodeName] = [obj[nodeName]];
          }
          obj[nodeName].push(xml.objectify(node.childNodes[i]));
        }
      }
    }
    if (count == 1 && typeof(obj["#text"]) != "undefined") {
      return obj["#text-node"];
    }
    if (count == 1 && typeof(obj["#cdata-section"]) != "undefined") {
      return obj["#cdata-section"];
    }
    if (typeof(obj["#cdata-section"]) != "undefined") {
      obj._value_ = obj.encoding == "base64" ? atob(obj["#cdata-section"]) : obj["#cdata-section"];
    }
    return obj;
  }
  return {nodeType: node.nodeType};
};
